DA_32     equ 4000h ; 32位
DA_DR		  EQU	90h	; 存在的只读数据段类型值
DA_DRW		EQU	92h	; 存在的可读写数据段属性值
DA_DRWA		EQU	93h	; 存在的已访问可读写数据段类型值
DA_C		  EQU	98h	; 存在的只执行代码段属性值
DA_CR		  EQU	9Ah	; 存在的可执行可读代码段属性值
DA_CCO		EQU	9Ch	; 存在的只执行一致代码段属性值
DA_CCOR		EQU	9Eh	; 存在的可执行可读一致代码段属性值

; 段基址 32 bit
; 段界限 20 bit
; 属性 12 bit 
; 一个描述符 64 bit
%macro Descriptor 3
  ;段界限 (2字节)
  dw %2 & 0FFFFh
  ;段基址1 (2字节)
  dw %1 & 0ffffh
  ;段基址2 (1字节)
  db (%1 >> 16) & 0ffh
  ;属性1 + 段界限2 (2字节)
  dw ((%2 >> 8 ) & 0f00h) | (%3 & 0f0ffh)
  ;段基址3
  db (%1 >> 24) & 0ffh
%endmacro

; 因为dos下调试程序，0100是可用区域
org 0100h 
  jmp PM_BEGIN

[SECTION .gdt]
;GDT                          段基址              段界限             属性
PM_GDT:         Descriptor      0,                   0,               0
PM_DESC_CODE32: Descriptor      0,           SegCode32Len - 1,    DA_C + DA_32
PM_DESC_DATA:   Descriptor      0,            DATALen -1,         DA_DRW
PM_DESC_STACK:   Descriptor     0,            TopOfStack,         DA_DRWA + DA_32
PM_DESC_TEST:   Descriptor      0200000h,            0ffffh,      DA_DRW
PM_DESC_VIDEO:   Descriptor     0B8000h,            0ffffh,       DA_DRW
;end of definition GDT
GDTLEN EQU $ - PM_GDT
GdtPtr dw GDTLEN - 1

dd 0 ; GDT 基地址

;GDT 选择子 (对GDT的偏移量)
SelectorCode32 equ PM_DESC_CODE32 - PM_GDT
SelectorData equ PM_DESC_DATA - PM_GDT
SelectorStack equ PM_DESC_STACK - PM_GDT
SelectorTest equ PM_DESC_TEST - PM_GDT
SelectorVideo equ PM_DESC_VIDEO - PM_GDT
; end of [SECTION .gdt]

; 数据
[SECTION .data1]
ALIGN 32
[BITS 32]
PM_DATA:
  PMMessage : db "Potect mode",0
  OffSetPMMessage equ PMMessage - $$
  DATALen equ $ - PM_DATA
; end of [SECTION .data1]

;全局的堆栈
[SECTION .gs]
ALIGN 32
[BITS 32]
PM_STACK:
  times 512 db 0
  TopOfStack equ $ - PM_STACK - 1
; end of [SECTION .gs]

[SECTION .s16]
[BITS 16]
PM_BEGIN:
	mov ax,cs
	mov ds,ax
	mov es,ax
	mov ss,ax
	mov sp,0100h
	
	;初始化32位的代码段
	xor eax,eax
	mov ax,cs
	shl eax,4
	add eax,PM_SEG_CODE32
	mov word[PM_DESC_CODE32+2],ax
	shr eax,16
	mov byte [PM_DESC_CODE32+4],al
	mov byte [PM_DESC_CODE32+7],ah
	
	
	;初始化32位的数据段
	xor eax,eax
	mov ax,ds
	shl eax,4
	add eax,PM_DATA
	mov word[PM_DESC_DATA+2],ax
	shr eax,16
	mov byte [PM_DESC_DATA+4],al
	mov byte [PM_DESC_DATA+7],ah
	
	;初始化32位的stack段
	xor eax,eax
	mov ax,ds
	shl eax,4
	add eax,PM_STACK
	mov word[PM_DESC_STACK+2],ax
	shr eax,16
	mov byte [PM_DESC_STACK+4],al
	mov byte [PM_DESC_STACK+7],ah
	
	;加载GDTR
	xor eax,eax
	mov ax,ds
	shl eax,4
	add eax,PM_GDT
	mov dword [GdtPtr +2 ],eax
	lgdt [GdtPtr]

  ; A20
  cli
  in al,92h
  or al,00000010b
  out 92h,al

  ;切换到保护模式
  mov eax,cr0
  or eax,1
  mov cr0,eax
  jmp dword SelectorCode32:0
; end of [SECTION .s16]

[SECTION .s32]
[BITS 32]
PM_SEG_CODE32:
  ; 通过数据段选择子放入ds，实现 段 + 偏移 寻址
  mov ax,SelectorData
  mov ds,ax
  
  mov ax,SelectorTest
  mov es,ax

  mov ax,SelectorVideo
  mov gs,ax

  mov ax,SelectorStack
  mov ss,ax
  mov esp,TopOfStack

  mov ah,0ch
  xor esi,esi
  xor edi,edi
  mov esi,OffSetPMMessage
  mov edi,(80*10+0) * 2
  cld

.1:
	lodsb
	test al,al
	jz .2
	mov [gs:edi],ax
	add edi,2
	jmp .1

.2:
  ;测试段的寻址
	mov ax, 'A'
	mov [es:0],ax
	mov ax,SelectorVideo
	mov gs,ax 
	mov edi,(80*15 +0)
	mov ah,0Ch
	mov al,[es:0]
	mov [gs:edi],ax

	jmp $

SegCode32Len equ $ - PM_SEG_CODE32